package config

import (
	"encoding/json"
	"fmt"
	fun "gitee.com/jishulang/go-fun"
	toml "github.com/pelletier/go-toml/v2"
	yaml "gopkg.in/yaml.v3"
	"io/ioutil"
	"os"
	"strings"
)

const CONFIG_DIR = "config"
const SETTINGS_NAME = "settings"
const (
	CONFIG_TYPE_YAML = "yaml"
	CONFIG_TYPE_YML  = "yml"
	CONFIG_TYPE_JSON = "json"
	CONFIG_TYPE_TOML = "toml"
)

var envArr = []string{"local", "dev", "uat", "prod"}
var supportedExts = []string{CONFIG_TYPE_YAML, CONFIG_TYPE_YML, CONFIG_TYPE_JSON, CONFIG_TYPE_TOML} // , "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"

func NewConfigYaml(conf interface{}) {
	NewConfig(conf, CONFIG_TYPE_YAML)
}

func NewConfigYml(conf interface{}) {
	NewConfig(conf, CONFIG_TYPE_YML)
}

func NewConfigJson(conf interface{}) {
	NewConfig(conf, CONFIG_TYPE_JSON)
}

func NewConfigToml(conf interface{}) {
	NewConfig(conf, CONFIG_TYPE_TOML)
}

// new一个配置
func NewConfig(conf interface{}, configType string) {
	fmt.Println("===========================conf start=============================")
	defer func() {
		b, err := json.MarshalIndent(conf, "", "  ")
		if err != nil {
			fmt.Println(fmt.Sprintf("conf:%v", conf))
		} else {
			fmt.Println("conf:", string(b))
		}
		fmt.Println("===========================conf end=============================")
	}()

	//
	env := os.Getenv("Environment")
	configType = strings.ToLower(configType)

	//
	configCheck(conf, configType)

	//
	configCreateDir()

	//
	envCreate(&env)

	//
	configCreateFile(conf, configType)

	//
	configGetSettings(conf, configType, env)

	//
	envFrom(conf)

	//
	verifyBinding(conf)

}

func configCheck(conf interface{}, configType string) {
	//
	if !fun.InterfaceIsPtr(conf) {
		panic("[NewConfig]parameter `conf` not Ptr type")
	}

	//
	if !fun.InterfaceIsStruct(conf) {
		panic("[NewConfig]parameter `conf` not Struct type")
	}

	//
	if configType == "" {
		panic("[NewConfig]configType empty")
	}

	//
	is := false
	for _, ext := range supportedExts {
		if configType == ext {
			is = true
		}
	}
	if !is {
		msg := fmt.Sprintf("[NewConfig]parameter `configType` wrong! SupportedExts:%v", supportedExts)
		panic(msg)
	}
}

func configCreateDir() {
	is, _ := fun.DirIsExist(CONFIG_DIR)
	if !is {
		err := fun.DirCreate(CONFIG_DIR, 0644)
		if err != nil {
			msg := fmt.Sprintf("[NewConfig]configCreate DirCreate err:%s", err.Error())
			panic(msg)
		}
	}
}

func configGetSettings(conf interface{}, configType string, env string) {
	filePath := ""
	switch configType {
	case CONFIG_TYPE_YAML, CONFIG_TYPE_YML:
		filePath = fmt.Sprintf("%s/%s.%s", CONFIG_DIR, SETTINGS_NAME, configType)
		readYaml(conf, filePath)

		filePath = fmt.Sprintf("%s/%s_%s.%s", CONFIG_DIR, SETTINGS_NAME, env, configType)
		is, _ := fun.FileIsExist(filePath)
		if is {
			readYaml(conf, filePath)
		}
	case CONFIG_TYPE_JSON:
		filePath = fmt.Sprintf("%s/%s.%s", CONFIG_DIR, SETTINGS_NAME, configType)
		readJson(conf, filePath)

		filePath = fmt.Sprintf("%s/%s_%s.%s", CONFIG_DIR, SETTINGS_NAME, env, configType)
		is, _ := fun.FileIsExist(filePath)
		if is {
			readJson(conf, filePath)
		}
	case CONFIG_TYPE_TOML:
		filePath = fmt.Sprintf("%s/%s.%s", CONFIG_DIR, SETTINGS_NAME, configType)
		readToml(conf, filePath)

		filePath = fmt.Sprintf("%s/%s_%s.%s", CONFIG_DIR, SETTINGS_NAME, env, configType)
		is, _ := fun.FileIsExist(filePath)
		if is {
			readToml(conf, filePath)
		}
	}
}

func readYaml(conf interface{}, filePath string) error {
	file, err := ioutil.ReadFile(filePath)
	if err != nil {
		msg := fmt.Sprintf("[NewConfig]ReadYaml ReadFile err:%s", err.Error())
		panic(msg)
	}

	//
	err = yaml.Unmarshal(file, conf)
	if err != nil {
		msg := fmt.Sprintf("[NewConfig]ReadYaml Unmarshal err:%s", err.Error())
		panic(msg)
	}
	return nil
}

func readJson(conf interface{}, filePath string) error {
	file, err := ioutil.ReadFile(filePath)
	if err != nil {
		msg := fmt.Sprintf("[NewConfig]ReadJson ReadFile err:%s", err.Error())
		panic(msg)
	}

	//
	err = json.Unmarshal(file, conf)
	if err != nil {
		msg := fmt.Sprintf("[NewConfig]ReadJson Unmarshal err:%s", err.Error())
		panic(msg)
	}
	return nil
}

func readToml(conf interface{}, filePath string) error {
	file, err := ioutil.ReadFile(filePath)
	if err != nil {
		msg := fmt.Sprintf("[NewConfig]ReadToml ReadFile err:%s", err.Error())
		panic(msg)
	}

	//
	err = toml.Unmarshal(file, conf)
	if err != nil {
		msg := fmt.Sprintf("[NewConfig]ReadToml Unmarshal err:%s", err.Error())
		panic(msg)
	}
	return nil
}
